﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using org.doubango.thialgou.commonWRAP;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace thialgou.lib.model
{
    public class EltMb : Elt
    {
        readonly UInt32 m_Address, m_X, m_Y;
        readonly UInt32[/*Y,U,V*/] m_Width, m_Height;
        readonly Int32[/*Y,U,V*/][/*Type*/][/*Data*/] m_Data;
        static int LINES_COUNT = Utils.GetMaxEnumValue<CommonYuvLine_t>();
        static int TYPES_COUNT = Utils.GetMaxEnumValue<CommonEltMbDataType_t>();

        public EltMb(CommonMb mb)
            : base(mb)
        {
            m_Address = mb.getAddress();
            m_X = mb.getX();
            m_Y = mb.getY();
            m_Data = new Int32[LINES_COUNT][][];
            m_Width = new UInt32[LINES_COUNT];
            m_Height = new UInt32[LINES_COUNT];

            for (int iLine = 0; iLine < LINES_COUNT; iLine++) // Y,U,V
            {
                m_Width[iLine] = mb.getWidth((CommonYuvLine_t)iLine);
                m_Height[iLine] = mb.getHeight((CommonYuvLine_t)iLine);

                m_Data[iLine] = new Int32[TYPES_COUNT][];
                for (int iType = 0; iType < TYPES_COUNT; iType++) // Final, Residual, Predicted...
                {
                    m_Data[iLine][iType] = new Int32[m_Width[iLine] * m_Height[iLine]];
#if COMPUTE_RESIDUAL
                    if ((int)CommonEltMbDataType_t.CommonEltMbDataType_Residual == iType)
                    {
                        // Residual not generated by the native code: do it ourself. Requires Final and Predicted.
                        int s = (int)(m_Width[iLine] * m_Height[iLine]);
                        for (int i = 0; i < s; i++)
                        {
                            m_Data[iLine][iType][i] = m_Data[iLine][(int)CommonEltMbDataType_t.CommonEltMbDataType_Final][i] - m_Data[iLine][(int)CommonEltMbDataType_t.CommonEltMbDataType_Predicted][i];
                        }
                    }
                    else
#endif
                    {
                        Marshal.Copy(mb.getData((CommonEltMbDataType_t)iType, (CommonYuvLine_t)iLine), m_Data[iLine][iType], 0, (int)(m_Width[iLine] * m_Height[iLine]));
                    }
                }
            }
        }

        public UInt32 Address
        {
            get
            {
                return m_Address;
            }
        }

        public UInt32 X
        {
            get
            {
                return m_X;
            }
        }

        public UInt32 Y
        {
            get
            {
                return m_Y;
            }
        }

        public override String Description
        {
            get
            {
                return String.Format("Address={0}, X={1}, Y={2}*/", m_Address, m_X, m_Y);
            }
        }

        public Int32[] GetMbBits(CommonYuvLine_t line, CommonEltMbDataType_t type)
        {
            return m_Data[(int)line][(int)type];
        }

        public Int32[] GetMbBits4x4(CommonYuvLine_t line, CommonEltMbDataType_t type, uint x, uint y)
        {
            if (x >= (m_Width[(int)line] >> 2) || y >= (m_Height[(int)line] >> 2))
            {
                return null;
            }
            Int32[] bits = GetMbBits(line, type);
            Int32[] bits4x4 = new Int32[16];
            uint indexStart = ((y << 2) * m_Width[(int)line]) + (x << 2);
            uint k = 0;
            for (uint j = 0; j < 4; j++, indexStart += m_Width[(int)line])
            {
                for (uint i = 0; i < 4; i++)
                {
                    bits4x4[k++] = bits[indexStart + i];
                }
            }
            return bits4x4;
        }

        public void CopyAsU8(byte[] pictData, UInt32 pictWidthForLine, UInt32 pictHeightForLine, CommonYuvLine_t eLine, CommonEltMbDataType_t eType)
        {
            UInt32 nStartIndex = (m_X * m_Y * m_Height[(int)eLine] * m_Width[(int)eLine]);
            for (UInt32 j = 0; j < m_Height[(int)eLine]; ++j)
            {
                for (UInt32 i = 0; i < m_Width[(int)eLine]; ++i)
                {
                    pictData[nStartIndex + i] = (byte)m_Data[(int)eLine][(int)eType][i * j];
                }
                nStartIndex += pictWidthForLine;
                if ((nStartIndex + pictWidthForLine) >= pictData.Length)
                {
                    break;
                }
            }            
        }

        public unsafe void CopyAsU8(IntPtr pictDataPtr, UInt32 pictWidthForLine, UInt32 pictHeightForLine, CommonYuvLine_t eLine, CommonEltMbDataType_t eType)
        {
            byte* _pictDataPtr = (byte*)pictDataPtr.ToPointer();

            UInt32 nPicStartIndex = (m_Y * (pictWidthForLine * m_Height[(int)eLine])) + (m_X * m_Width[(int)eLine]);
            UInt32 nLastIndex = (pictWidthForLine * pictHeightForLine);
            UInt32 nMbStartIndex = 0;
            // if (nStartIndex + ())
            for (UInt32 j = 0; j < m_Height[(int)eLine]; ++j)
            {
                for (UInt32 i = 0; i < m_Width[(int)eLine]; ++i)
                {
                    fixed (Int32* pMb = m_Data[(int)eLine][(int)eType])
                    {
                        _pictDataPtr[nPicStartIndex + i] = (byte)pMb[nMbStartIndex++];
                    }
                }
                if ((nPicStartIndex + pictWidthForLine) > nLastIndex)
                {
                    break;
                }
                nPicStartIndex += pictWidthForLine;
            }
        }

        public int CompareTo(EltMb other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other");
            }
            return (Address.CompareTo(other.Address));
        }
    }
}
